package com.shiroexploit.gui;

import com.shiroexploit.task.TestConnectionTask;
import com.shiroexploit.util.*;
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import org.controlsfx.control.CheckComboBox;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

public class StartPane extends Application {
    private static final int HEIGHT = 35;
    private Config config = Config.getInstance();
    private BorderPane borderPane = new BorderPane();
    private BorderPane simpleRequestSubpane = new BorderPane();
    private BorderPane complexRequestSubpane = new BorderPane();
    private ComboBox<String> comboBox = new ComboBox<>();
    private CheckBox complexHttpRequest = new CheckBox("复杂Http请求");
    private CheckBox useHttps = new CheckBox("使用Https");
    private CheckBox specifyKeyAndGadget = new CheckBox("指定Key/Gadget/EchoType");
    private CheckBox useBigKeyFile = new CheckBox("使用 keys.conf.big");
    private TextField urlTextField = new TextField();
    private TextArea cookieField = new TextArea();
    private TextArea requestBodyField = new TextArea();
    private ConfigPane configPane;
    private ProxyConfigPane proxyConfigPane;
    private Button next = new Button("下一步");
    private CheckComboBox<String> keyComboBox = new CheckComboBox<>();
    private Button cleanKeySelection = new Button("重置");
    private CheckComboBox<String> gadgetComboBox = new CheckComboBox<>();
    private Button cleanGadgetSelection = new Button("重置");
    private CheckComboBox<String> echoTypeComboBox = new CheckComboBox<>();
    private Button cleanEchoTypeSelection = new Button("重置");
    private Button proxy = new Button("设置代理");
    private Button about = new Button("关于");


    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws Exception {

        Pane borderPane = new StartPane().getPane();
        primaryStage.setTitle("Shiro550/721漏洞检测 v2.51 Final  by飞鸿");
        primaryStage.setScene(new Scene(borderPane, 800, 700));
        primaryStage.show();

//        //测试
//        MainPane pane = new MainPane();
//        primaryStage.setTitle("Shiro550/721漏洞检测  by飞鸿");
//        primaryStage.setScene(new Scene(pane.getPane(), 800,600));
//        primaryStage.show();
    }

    public StartPane(){
        drawPane();
        addListeners();
    }

    public Pane getPane() {
        return borderPane;
    }

    private void drawPane(){
        borderPane.setPadding(new Insets(10,10,10,10));

        Label label = new Label("选择要验证的漏洞类型");
        List<String> data = new ArrayList<>();
        data.add("Shiro550");
        data.add("Shiro721");
        comboBox.setItems(FXCollections.observableArrayList(data));
        comboBox.setPrefHeight(HEIGHT);
        comboBox.setPrefWidth(150);
        comboBox.getSelectionModel().select(0);

        GridPane leftGridPane = new GridPane();
        leftGridPane.setAlignment(Pos.CENTER_LEFT);
        leftGridPane.setHgap(20);
        leftGridPane.setVgap(10);
        leftGridPane.setPadding(new Insets(10,10,10,10));

        useHttps.setDisable(true);

        leftGridPane.add(label, 0,0);
        leftGridPane.add(comboBox,1,0);
        leftGridPane.setColumnSpan(complexHttpRequest, 2);
        leftGridPane.add(complexHttpRequest,0,1);
        leftGridPane.setColumnSpan(useHttps, 2);
        leftGridPane.add(useHttps, 0, 2);
        leftGridPane.setColumnSpan(useBigKeyFile, 2);
        leftGridPane.add(useBigKeyFile, 0,3);
        leftGridPane.setColumnSpan(specifyKeyAndGadget, 2);
        leftGridPane.add(specifyKeyAndGadget, 0, 4);

        keyComboBox.getItems().addAll(FXCollections.observableArrayList(config.getKeys()));
        keyComboBox.setTitle("指定Key");
        keyComboBox.setPrefWidth(280);
        keyComboBox.setDisable(true);

        gadgetComboBox.getItems().addAll(Tools.getPayloadNames());
        gadgetComboBox.setTitle("指定Gadget");
        gadgetComboBox.setDisable(true);
        gadgetComboBox.setPrefWidth(280);

        echoTypeComboBox.getItems().addAll(Tools.getEchoTypes());
        echoTypeComboBox.setTitle("指定回显方式");
        echoTypeComboBox.setDisable(true);
        echoTypeComboBox.setPrefWidth(280);

        cleanKeySelection.setDisable(true);
        cleanGadgetSelection.setDisable(true);
        cleanEchoTypeSelection.setDisable(true);

        GridPane rightGridPane = new GridPane();
        rightGridPane.setAlignment(Pos.CENTER_RIGHT);
        rightGridPane.setHgap(20);
        rightGridPane.setVgap(10);
        rightGridPane.setPadding(new Insets(10,0,10,0));

        HBox hBox = new HBox();
        hBox.setAlignment(Pos.CENTER_RIGHT);
        hBox.getChildren().addAll(proxy, about);
        hBox.setMargin(about, new Insets(0,10,0,10));
        rightGridPane.setColumnSpan(hBox,2);
        rightGridPane.add(hBox, 0, 0);
        rightGridPane.add(keyComboBox, 0, 1);
        rightGridPane.add(cleanKeySelection, 1,1);
        rightGridPane.add(gadgetComboBox, 0,2);
        rightGridPane.add(cleanGadgetSelection, 1,2);
        rightGridPane.add(echoTypeComboBox, 0,3);
        rightGridPane.add(cleanEchoTypeSelection, 1, 3);

        HBox topPane = new HBox();
        topPane.setSpacing(40);
        topPane.getChildren().addAll(leftGridPane, rightGridPane);

        urlTextField.setPrefHeight(35);
        urlTextField.setPromptText("目标地址");
        cookieField.setPrefHeight(300);
        cookieField.setWrapText(true);
        cookieField.setPromptText("rememberMe=dGhpcyBpcyBhIGRlbW9uc3RyYXRpb24gc3RyaW5nCg==");
        cookieField.setDisable(true);
        simpleRequestSubpane.setPadding(new Insets(10,10,10,10));
        simpleRequestSubpane.setTop(urlTextField);
        simpleRequestSubpane.setMargin(urlTextField, new Insets(0,0,20,0));
        simpleRequestSubpane.setCenter(cookieField);

        requestBodyField.setPrefHeight(350);
        requestBodyField.setWrapText(true);
        requestBodyField.setPromptText("POST /someurl HTTP/1.1\r\n" +
                "Host: passport.feihong.com\r\n" +
                "Connection: close\r\n" +
                "Accept: application/json, text/javascript, */*; q=0.01\r\n" +
                "Accept-Encoding: gzip, deflate\r\n" +
                "Accept-Language: zh-CN,zh;q=0.9\r\n" +
                "Cookie: x-zp-client-id=bea678e6-7fa8-4cfd-8d23-5d98f8876702; rememberMe=dGhpcyBpcyBhIGRlbW9uc3RyYXRpb24gc3RyaW5nCg==\r\n\r\n" +
                "param1=value1&param2=value2\r\n\r\n");
        complexRequestSubpane.setPadding(new Insets(10,10,10,10));
        complexRequestSubpane.setCenter(requestBodyField);

        borderPane.setTop(topPane);
        borderPane.setCenter(simpleRequestSubpane);

        HBox hbox = new HBox();
        hbox.getChildren().add(this.next);
        hbox.setAlignment(Pos.CENTER);
        borderPane.setBottom(hbox);
        borderPane.setMargin(hbox, new Insets(10,0,10,0));
    }

    private void addListeners(){
        comboBox.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<String>() {
            @Override
            public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
                if(newValue.equalsIgnoreCase("shiro721")){
                    cookieField.setDisable(false);
                }

                if(newValue.equalsIgnoreCase("shiro550")){
                    cookieField.setDisable(true);
                }
            }
        });


        complexHttpRequest.selectedProperty().addListener(new ChangeListener<Boolean>() {
            @Override
            public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
               if(newValue == true){
                   borderPane.setCenter(complexRequestSubpane);
                   useHttps.setDisable(false);
               }else{
                   borderPane.setCenter(simpleRequestSubpane);
                   useHttps.setSelected(false);
                   useHttps.setDisable(true);
               }
            }
        });


        useBigKeyFile.selectedProperty().addListener(new ChangeListener<Boolean>() {
            @Override
            public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
                if(newValue == true){
                    config.setKeys(ConfigReader.loadKeys(1));
                    keyComboBox.setTitle("指定Key");
                }else{
                    config.setKeys(ConfigReader.loadKeys(0));
                    keyComboBox.setTitle("指定Key");
                }

                keyComboBox.getItems().clear();
                keyComboBox.getItems().addAll(config.getKeys());
            }
        });

        specifyKeyAndGadget.selectedProperty().addListener(new ChangeListener<Boolean>() {
            @Override
            public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
                if(newValue == true){
                    keyComboBox.setDisable(false);
                    gadgetComboBox.setDisable(false);
                    echoTypeComboBox.setDisable(false);

                    cleanKeySelection.setDisable(false);
                    cleanGadgetSelection.setDisable(false);
                    cleanEchoTypeSelection.setDisable(false);
                }else{
                    keyComboBox.setDisable(true);
                    gadgetComboBox.setDisable(true);
                    echoTypeComboBox.setDisable(true);

                    cleanKeySelection.setDisable(true);
                    cleanGadgetSelection.setDisable(true);
                    cleanEchoTypeSelection.setDisable(true);
                }
            }
        });

        cleanKeySelection.setOnAction(event -> {
            keyComboBox.getCheckModel().clearChecks();
        });

        cleanGadgetSelection.setOnAction(event -> {
            gadgetComboBox.getCheckModel().clearChecks();
        });

        cleanEchoTypeSelection.setOnAction(event -> {
            echoTypeComboBox.getCheckModel().clearChecks();
        });

        proxy.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {

                if(proxyConfigPane == null){
                    Stage newStage = new Stage();
                    proxyConfigPane = new ProxyConfigPane(StartPane.this);
                    Pane pane = proxyConfigPane.getPane();
                    newStage.setTitle("设置代理");
                    newStage.setScene(new Scene(pane, 400, 250));
                    newStage.show();
                }else{
                    Stage next = (Stage)proxyConfigPane.getPane().getScene().getWindow();
                    proxyConfigPane.update();
                    next.show();
                }
            }
        });

        about.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                Stage newStage = new Stage();
                AboutPane aboutPane = new AboutPane();
                Pane pane = aboutPane.getPane();
                newStage.setTitle("关于");
                Scene scene = new Scene(pane, 600, 500);
                scene.getStylesheets().add(getClass().getResource("/my.css").toExternalForm());
                newStage.setScene(scene);
                newStage.show();
            }
        });

        next.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                config.setVulType(comboBox.getSelectionModel().getSelectedIndex());

                if(specifyKeyAndGadget.isSelected()){
                    ObservableList<String> selected = keyComboBox.getCheckModel().getCheckedItems();
                    if(selected.size() != 0){
                        config.setKeys(selected);
                    }

                    selected = gadgetComboBox.getCheckModel().getCheckedItems();
                    if(selected.size() != 0){
                        List<PayloadType> list = new ArrayList<>();
                        for(String str : selected){
                            list.add(PayloadType.valueOf(str));
                        }

                        config.setGadgets(list);
                    }


                    selected = echoTypeComboBox.getCheckModel().getCheckedItems();
                    if(selected.size() != 0){
                        List<EchoType> list = new ArrayList<>();
                        for(String str : selected){
                            list.add(EchoType.valueOf(str));
                        }

                        config.setEchoTypes(list);
                    }
                }

                Stage currentStage = (Stage)borderPane.getScene().getWindow();
                if(!complexHttpRequest.isSelected()){
                    String url = urlTextField.getText();
                    url = url != null ? url.trim() : null;
                    String cookie = cookieField.getText();
                    cookie = cookie != null ? cookie.trim() : null;

                    boolean bool = validateURL(url);
                    if(comboBox.getSelectionModel().getSelectedIndex() == 1){
                        bool = bool && validateCookie(url, cookie);
                    }

                    if(bool) {
                        HttpRequestInfo httpRequestInfo = new HttpRequestInfo();
                        httpRequestInfo.setRequestMethod("GET");
                        httpRequestInfo.setRequestURL(url);
                        if(config.getVulType() == 1)
                            httpRequestInfo.setRememberMeCookie(Hex.encodeHexString(Base64.decodeBase64(cookie.split("=",2)[1])));
                        config.setRequestInfo(httpRequestInfo);

                        final TestConnectionTask task = new TestConnectionTask(httpRequestInfo);
                        task.valueProperty().addListener(new ChangeListener<Integer>() {
                            @Override
                            public void changed(ObservableValue<? extends Integer> observable, Integer oldValue, Integer newValue) {
                                if(task.getStatus() == 1){
                                    nextStage();
                                }

                                if(task.getStatus() == -1){
                                    PromptMessageUI.getAlert("目标地址无法访问","您输入的 URL 无法正常访问");
                                }
                            }
                        });

                        PenddingUI penddingUI = new PenddingUI(task, currentStage);
                        penddingUI.activateProgressBar();
                    }
                }else{
                    String requestBody = requestBodyField.getText();
                    HttpRequestInfo httpRequestInfo = new HttpRequestInfo();

                    if(requestBody == null || requestBody.trim().equals("")){
                        PromptMessageUI.getAlert("HTTP请求不能为空","请输入一个有效的HTTP请求");
                        return;
                    }

                    try{
                        httpRequestInfo.parse(requestBody, useHttps.isSelected());
                    }catch (Exception e){
                        PromptMessageUI.getAlert("HTTP请求格式不正确","请输入一个格式正确的HTTP请求");
                        return;
                    }

                    if(comboBox.getSelectionModel().getSelectedIndex() == 1){
                        if(httpRequestInfo.getRememberMeCookie() == null){
                            PromptMessageUI.getAlert("缺失有效的rememberMe Cookie","未提供rememberMe Cookie或者提供的remeberMe Cookie格式不正确");
                            return;
                        }
                    }

                    config.setRequestInfo(httpRequestInfo);

                    final TestConnectionTask task = new TestConnectionTask(httpRequestInfo);
                    task.valueProperty().addListener(new ChangeListener<Integer>() {
                        @Override
                        public void changed(ObservableValue<? extends Integer> observable, Integer oldValue, Integer newValue) {
                            if(task.getStatus() == 1){
                                nextStage();
                            }

                            if(task.getStatus() == -1){
                                PromptMessageUI.getAlert("目标地址无法访问","您输入的 Http 请求解析后无法正常访问");
                            }
                        }
                    });

                    PenddingUI penddingUI = new PenddingUI(task, currentStage);
                    penddingUI.activateProgressBar();

                }
            }
        });
    }

    private void nextStage() {
        Stage currentStage = (Stage) borderPane.getScene().getWindow();

        if(configPane == null){
            Stage newStage = new Stage();
            configPane = new ConfigPane(StartPane.this);
            Pane pane = configPane.getPane();
            newStage.setTitle(Config.getInstance().getRequestInfo().getRequestURL());
            newStage.setScene(new Scene(pane, 550, 500));
            currentStage.hide();
            newStage.show();
        }else{
            currentStage.hide();
            Stage next = (Stage)configPane.getPane().getScene().getWindow();
            next.show();
        }

    }

    private boolean validateURL(String url){
        if(url == null || url.trim().equals("")){
            PromptMessageUI.getAlert("URL不能为空","请输入一个有效的URL");
            return false;
        }

        if(!url.startsWith("http://") && !url.startsWith("https://")){
            PromptMessageUI.getAlert("URL格式不正确","请输入完整的URL，包括http(s)前缀");
            return false;
        }

        try{
            URL u = new URL(url);
        } catch (MalformedURLException e) {
            PromptMessageUI.getAlert("URL格式不正确","请输入正确格式的URL");
            return false;
        }

        return true;
    }


    private boolean validateCookie(String url, String cookie){
        if(cookie == null || cookie.trim().equals("")){
            PromptMessageUI.getAlert("Cookie不能为空","请输入一个有效的rememberMe Cookie");
            return false;
        }

        if(!cookie.startsWith(config.getRememberMeCookieName() + "=")){
            PromptMessageUI.getAlert("cookie格式不正确","请输入正确格式的rememberMe cookie");
            return false;
        }

        return true;
    }
}